package com.liu.queue.codec;

/**
 * @param
 * @BelongsProject: MessageQueueDEMO
 * @BelongsPackage: com.liu.producer.codec
 * @author: L
 * @Date: 2023/10/14 23:22
 * @return
 */

import com.liu.common.Constants;
import com.liu.queue.agreement.Agreement;
import com.liu.queue.message.PushMessage;
import com.liu.queue.message.MessageTypeEnum;
import com.liu.serialization.Serialization;
import com.liu.serialization.SerializationProvider;
import com.liu.serialization.SerializationTypeEnum;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.List;

/**
 * @param
 * @BelongsProject: MessageQueueDEMO
 * @BelongsPackage: com.liu.producer.codec
 * @author: L
 * @Date: 2023/10/14 23:22
 * @return
 */

public class MessageCodec extends MessageToMessageCodec<ByteBuf, Agreement> {
    @Override
    protected void encode(ChannelHandlerContext ctx, Agreement agreement, List<Object> out) {
        System.out.println("---->encode");
        ByteBuf buffer = ctx.alloc().buffer();
        // 魔数 4
        buffer.writeInt(agreement.getMagicNumber());
        // 版本号 1
        buffer.writeByte(agreement.getVersion());
        // 消息的类型 1
        buffer.writeByte(agreement.getMessageType().getType());
        // 序列化类型 1
        SerializationTypeEnum serializationType = agreement.getSerializationType();
        buffer.writeByte(serializationType.getType());
        // 消息主题 4
        buffer.writeBytes(agreement.getTopic());
        // 消息ID 8
        buffer.writeLong(agreement.getMessageID());
        // 消息体长度值固定 4
        // 消息体处理
        if (null == agreement.getBody()) {
            // 消息体长度
            buffer.writeInt(0);
        } else {
            // 获取对应序列化器
            Serialization serialization = SerializationProvider.getSerialization(serializationType);
            byte[] bytes = serialization.serialization(agreement.getBody());
            // 消息体长度
            buffer.writeInt(bytes.length);
            buffer.writeBytes(bytes);
            // buffer.writeCharSequence()
            // serialization.serialization()
        }
        out.add(buffer);
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) {
        System.out.println("---->decode");
        // 魔数
        int magicNumber = byteBuf.readInt();
        // 版本
        byte version = byteBuf.readByte();
        if (magicNumber != Constants.MAGIC_NUMBER || version != Constants.VERSION) {
            return;
        }
        // 消息类型
        MessageTypeEnum messageType = MessageTypeEnum.get(byteBuf.readByte());
        // 序列化类型
        SerializationTypeEnum serializationType = SerializationTypeEnum.get(byteBuf.readByte());
        // 消息主题
        byte[] topic = new byte[4];
        byteBuf.readBytes(topic);
        // 消息ID
        long messageID = byteBuf.readLong();
        // 消息体长度
        int bodyLength = byteBuf.readInt();
        if (bodyLength <= 0) {
            return;
        }
        // 消息体 返回状态码
        byte[] bytes = new byte[bodyLength];
        byteBuf.readBytes(bytes);


        // 此处根据请求类型创建对应的类,推送请求,拉取请求
        // 必须继承Message类,因为Netty的Handler是根据传入的泛型决定是否进行处理,所以此处生成对应类方便进行处理
        MessageTypeEnum messageTypeEnum = MessageTypeEnum.get(messageType);
        Class<?> clazz = messageTypeEnum.getClazz();
        PushMessage pushMessage = null;
        try {
            Constructor constructor = clazz.getConstructor();
            pushMessage = (PushMessage) constructor.newInstance();
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        pushMessage.setMessageID(messageID);
        pushMessage.setSerializationType(serializationType);
        pushMessage.setMessageType(messageType);
        pushMessage.setTopic(topic);
        pushMessage.setBody(bytes);
        out.add(pushMessage);
    }
}